package org.xhy.retryerboot.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.xhy.retryer.core.Retryer;
import org.xhy.retryer.lister.RetryerLister;
import org.xhy.retryer.predicate.Predicates;
import org.xhy.retryer.strategy.ExponentialRetryerStrategy;
import org.xhy.retryer.strategy.RetryerStrategy;
import org.xhy.retryerboot.proxy.ProxyFactory;
import org.xhy.retryerboot.proxy.RetryerProxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

/**
 * @description:
 * @gitee: https://gitee.com/XhyQAQ
 * @copyright: B站: https://space.bilibili.com/152686439
 * @Author: Xhy
 * @CreateTime: 2024-04-30 22:18
 */
public class RetryerPostProcessor implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContextAware;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        final Class<?> aClass = bean.getClass();
        Map<Integer,Retryer> retryerHashMap = new HashMap<>();
        // 遍历每个方法
        for (Method method : aClass.getMethods()) {
            Retryer retryer = null;
            try {
                if ((retryer = defaultRetryerStrategyMatchRule(method))!=null){
                    retryerHashMap.put(method.hashCode(),retryer);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        // 生成代理对象
        if (!retryerHashMap.isEmpty()) {
            // 返回代理对象
            final RetryerProxy retryerProxy = new RetryerProxy(bean, retryerHashMap);
            return ProxyFactory.createdProxy(bean,retryerProxy);
        }
        return bean;
    }


    protected Retryer defaultRetryerStrategyMatchRule(Method method) throws Exception {
        RetryerStrategy retryerStrategy = null;
        if (method.isAnnotationPresent(org.xhy.retryerboot.annotation.Retryer.class)) {
            final org.xhy.retryerboot.annotation.Retryer retryerAnnotation = method.getAnnotation(org.xhy.retryerboot.annotation.Retryer.class);
            final int count = retryerAnnotation.maxAttempts();
            final int time = retryerAnnotation.time();
            final TimeUnit timeUnit = retryerAnnotation.timeUnit();
            final String[] result = retryerAnnotation.result();
            final Class[] resultClass = retryerAnnotation.resultClass();
            final String listerName = retryerAnnotation.listerName();
            if (result.length != resultClass.length){
                throw new Exception("重试结果和重试结果类型长度不匹配");
            }
            final Class[] throwss = retryerAnnotation.exception();
            final String async = retryerAnnotation.async();
            retryerStrategy = new ExponentialRetryerStrategy(count,time,timeUnit);

            final Predicate[] resultPredicates = new Predicate[result.length];
            for (int i = 0; i < result.length; i++) {
                try {
                    resultPredicates[i] = Predicates.equalTo(resultClass[i].getConstructor(String.class).newInstance(result[i]));
                } catch (InstantiationException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException(e);
                }
            }
            final Predicate resultPredicateObject = Predicates.or(resultPredicates);
            final Predicate[] throwssPredicates = new Predicate[throwss.length];
            for (int i = 0; i < throwssPredicates.length; i++) {
                throwssPredicates[i] = Predicates.throwsTo(throwss[i]);
            }
            final Predicate throwsResultPredicateObject = Predicates.or(throwssPredicates);
            Object threadPoll = null;
            if (!async.equals("")){
                threadPoll = applicationContextAware.getBean(async);
            }
            Object listerBean = null;
            if (!listerName.equals("")){
                listerBean = applicationContextAware.getBean(listerName);
            }
            if (listerBean !=null && !(listerBean instanceof RetryerLister)){
                throw new ClassCastException(listerBean + "不是 RetryerLister 子类");
            }

            if (threadPoll !=null && !(threadPoll instanceof  Executor)){
                throw new ClassCastException(threadPoll + "不是 Executor 子类");

            }
            return new Retryer(retryerStrategy,resultPredicateObject,throwsResultPredicateObject, (RetryerLister) listerBean, (Executor) threadPoll);
        }
        return null;
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContextAware = applicationContext;
    }
}
